﻿using System.Globalization;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Reflection;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DynamicLinq;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;

namespace Devonline.AspNetCore;

/// <summary>
/// 公共数据处理服务
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public class DataService<TDbContext, TEntitySet, TKey> :
    IDataService<TDbContext, TEntitySet, TKey>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<TKey>
    where TKey : IConvertible
{
    protected readonly TDbContext _context;
    protected readonly ILogger<DataService<TDbContext, TEntitySet, TKey>> _logger;
    protected readonly IDistributedCache _cache;
    protected readonly AppSetting _appSetting;
    protected readonly HttpContext _httpContext;
    protected readonly HttpRequest _request;
    protected readonly DbSet<TEntitySet> _dbSet;
    protected readonly Type _type;
    protected readonly string _typeName;
    protected readonly PropertyInfo[] _propertyInfos;

    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger">日志</param>
    /// <param name="context">数据库上下文</param>
    /// <param name="httpContextAccessor">Http上下文</param>
    /// <param name="cache">缓存</param>
    /// <param name="appSetting">配置</param>
    public DataService(
        TDbContext context,
        ILogger<DataService<TDbContext, TEntitySet, TKey>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        )
    {
        if (httpContextAccessor.HttpContext == null)
        {
            throw new ArgumentNullException(nameof(httpContextAccessor.HttpContext), "HttpContext can't be null!");
        }

        _context = context;
        _logger = logger;
        _cache = cache;
        _appSetting = appSetting;
        _httpContext = httpContextAccessor.HttpContext;
        _request = _httpContext.Request;
        UserName = GetUserName();
        EnableCache = _appSetting.EnableCache;
        _dbSet = _context.Set<TEntitySet>();
        _type = typeof(TEntitySet);
        _typeName = _type.GetDisplayName();
        _propertyInfos = _type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        EnableCache = EnableCache && _type.HasAttribute<CacheableAttribute>();
    }

    #region 基础属性和方法, 业务无关属性和方法
    /// <summary>
    /// 当前对象是否启用了缓存
    /// </summary>
    public virtual bool EnableCache { get; }
    /// <summary>
    /// 当前登录的用户
    /// </summary>
    public virtual string UserName { get; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// </summary>
    /// <returns></returns>
    public virtual string? GetUserId() => _httpContext.GetUserId();
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 userName
    /// </summary>
    /// <returns></returns>
    public virtual string GetUserName() => _httpContext.GetUserName();
    /// <summary>
    /// 从当前 request 中获取参数的值, 取值顺序 Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T? GetRequestOption<T>(string key) where T : IConvertible
    {
        var value = string.Empty;
        if (_request.Query.Keys.Contains(key))
        {
            value = _request.Query[key];
        }
        else if (_request.HasFormContentType && _request.Form.Keys.IsNotNullOrEmpty() && _request.Form.Keys.Contains(key))
        {
            value = _request.Form[key];
        }
        else if (_request.Headers.IsNotNullOrEmpty() && _request.Headers.ContainsKey(key))
        {
            value = _request.Headers[key];
        }
        else if (_request.Cookies.IsNotNullOrEmpty() && _request.Cookies.Keys.Contains(key))
        {
            value = _request.Cookies[key];
        }

        return string.IsNullOrWhiteSpace(value) ? default : value.To<T>();
    }
    /// <summary>
    /// 获取上下文对象中参数/变量的值, 取值顺序 HttpContext -> Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T? GetContextOption<T>(string key) where T : IConvertible
    {
        var value = _httpContext.User.Claims.FirstOrDefault(c => c.Type == key)?.Value;
        return string.IsNullOrWhiteSpace(value) ? _httpContext.Request.GetRequestOption<T>(key) : value.To<T>();
    }
    /// <summary>
    /// 获取查询选项列表
    /// </summary>
    /// <returns>返回合并后的查询选项</returns>
    public virtual IEnumerable<KeyValuePair<string, StringValues>> GetRequestOptions()
    {
        var queryString = _request.Query.ToList();
        if (_request.HasFormContentType && (_request.Form.Keys?.Count ?? 0) > 0)
        {
            queryString.AddRange(_request.Form);
        }

        if (_request.Headers.IsNotNullOrEmpty())
        {
            queryString.AddRange(_request.Headers);
        }

        if (_request.Cookies.IsNotNullOrEmpty())
        {
            queryString.AddRange(_request.Cookies.ToDictionary(x => x.Key, x => new StringValues(x.Value)));
        }

        return queryString;
    }
    /// <summary>
    /// 获取附件绝对地址
    /// </summary>
    /// <param name="fileName">附件文件名</param>
    /// <returns></returns>
    public virtual string GetAttachmentPath(string fileName) => Path.Combine((_appSetting.Attachment?.RootPath ?? AppSettings.DEFAULT_ATTACHMENT_PATH).GetAbsolutePath(), fileName);
    /// <summary>
    /// 获取随机的临时文件名
    /// </summary>
    /// <param name="extension">文件扩展名</param>
    /// <returns></returns>
    public virtual string GetTempFileName(string? extension = default)
    {
        var filePath = DateTime.UtcNow.ToString(AppSettings.DEFAULT_DATE_FORMAT, CultureInfo.InvariantCulture);
        var attachmentPath = GetAttachmentPath(filePath);
        if (!Directory.Exists(attachmentPath))
        {
            Directory.CreateDirectory(attachmentPath);
        }

        extension ??= AppSettings.DEFAULT_TEMPORARY_FILE_EXTENSION;
        return Path.Combine(filePath, KeyGenerator.GetKey() + extension);
    }
    /// <summary>
    /// 从分布式缓存获取值
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns></returns>
    public virtual async Task<TValue?> GetCacheAsync<TValue>(string key) => await _cache.GetValueAsync<TValue>(key);
    /// <summary>
    /// 将值写入分布式缓存, 带预设过期时间
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    public virtual async Task SetCacheAsync<TValue>(string key, TValue value) => await _cache.SetStringAsync(key, value.ToJsonString(), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(_appSetting.CacheExpireTime) });
    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <returns></returns>
    public virtual async Task RefreshCacheAsync()
    {
        if (EnableCache)
        {
            var cacheKey = AppSettings.CACHE_DATA + _type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = _dbSet.ToHashSet();
            await _cache.SetStringAsync(cacheKey, entitySets.ToJsonString());
            _logger.LogInformation("{typeName} has been finished refresh cache data", _typeName);
        }
    }
    /// <summary>
    /// 提交当前操作
    /// </summary>
    /// <returns></returns>
    public virtual Task<int> SaveChangesAsync() => _context.SaveChangesAsync();
    #endregion

    #region 基础数据操作方法, 面向当前业务类型
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <returns></returns>
    public virtual TypeAdapterConfig GetAdapterConfig()
    {
        var config = new TypeAdapterConfig();
        var typeConfig = config.ForDestinationType<TEntitySet>()
            //.PreserveReference(true)
            //.ShallowCopyForSameType(true)
            .AvoidInlineMapping(true)
            //.IgnoreAttribute(typeof(NotMappedAttribute))
            //.Ignore(nameof(Personal.Additionals))
            .MaxDepth(2);

        if (_type.IsFromType<IEntitySetWithCreate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreate<TKey>.RowVersion), nameof(IEntitySetWithCreate<TKey>.CreatedBy), nameof(IEntitySetWithCreate<TKey>.CreatedOn));
        }

        if (_type.IsFromType<IEntitySetWithCreateAndUpdate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedBy), nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedOn));
        }

        return config;
    }
    /// <summary>
    /// 从当前上下文解除当前对象的跟踪状态
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    public virtual void Detach(TEntitySet entitySet)
    {
        var exist = _dbSet.Local?.FirstOrDefault(x => x.Id.Equals(entitySet.Id));
        if (exist != null)
        {
            _context.Entry(exist).State = EntityState.Detached;
        }
    }
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    public virtual void Create(TEntitySet entitySet)
    {
        if (entitySet is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            entitySetWithCreate.Create(UserName, _appSetting.DateTimeKind);
        }
    }
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    public virtual void Update(TEntitySet entitySet)
    {
        if (entitySet is IEntitySetWithCreateAndUpdate<TKey> entitySetWithCreateAndUpdate)
        {
            entitySetWithCreateAndUpdate.Update(UserName, _appSetting.DateTimeKind);
        }
    }
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    public virtual void Update(TEntitySet entitySet, bool isLogical = false)
    {
        if (isLogical && entitySet is IEntitySetWithCreate<TKey>)
        {
            //逻辑修改会产生一个当前数据不同主键的副本, 并设置为已更新的状态, 使用 RowVersion 字段关联更新后的数据编号
            var logicalCopy = entitySet.Copy();
            logicalCopy.Id = KeyGenerator.GetKey<TKey>();
            if (logicalCopy is IEntitySetWithCreate<TKey> entitySetWithCreate)
            {
                entitySetWithCreate.RowVersion = entitySet.Id;
                entitySetWithCreate.State = DataState.Updated;
            }

            _dbSet.Add(logicalCopy);
        }
    }
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual void Delete(TEntitySet entitySet, bool isLogical = false)
    {
        if (isLogical && entitySet is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            Update(entitySetWithCreate);
            entitySetWithCreate.State = DataState.Deleted;
            _dbSet.Update(entitySet);
        }
        else
        {
            _dbSet.Remove(entitySet);
        }
    }
    #endregion

    #region 数据查询方法, 面向非当前业务类型
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <returns></returns>
    public virtual TypeAdapterConfig GetAdapterConfig<TModel>() where TModel : class, IEntitySet<TKey>
    {
        var _type = typeof(TModel);
        var config = new TypeAdapterConfig();
        var typeConfig = config.ForDestinationType<TModel>()
            //.PreserveReference(true)
            //.ShallowCopyForSameType(true)
            .AvoidInlineMapping(true)
            //.IgnoreAttribute(typeof(NotMappedAttribute))
            //.Ignore(nameof(Personal.Additionals))
            .MaxDepth(2);

        if (_type.IsFromType<IEntitySetWithCreate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreate<TKey>.RowVersion), nameof(IEntitySetWithCreate<TKey>.CreatedBy), nameof(IEntitySetWithCreate<TKey>.CreatedOn));
        }

        if (_type.IsFromType<IEntitySetWithCreateAndUpdate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedBy), nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedOn));
        }

        return config;
    }
    /// <summary>
    /// 从当前上下文解除当前对象的跟踪状态
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    public virtual void Detach<TModel>(TModel model) where TModel : class, IEntitySet<TKey>
    {
        var exist = _context.Set<TModel>().Local.FirstOrDefault(x => x.Id.Equals(model.Id));
        if (exist != null)
        {
            _context.Entry(exist).State = EntityState.Detached;
        }
    }
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    public virtual void Create<TModel>(TModel model) where TModel : class, IEntitySet<TKey>
    {
        if (model is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            entitySetWithCreate.Create(UserName, _appSetting.DateTimeKind);
        }
    }
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    public virtual void Update<TModel>(TModel model) where TModel : class, IEntitySet<TKey>
    {
        if (model is IEntitySetWithCreateAndUpdate<TKey> entitySetWithCreateAndUpdate)
        {
            entitySetWithCreateAndUpdate.Update(UserName, _appSetting.DateTimeKind);
        }
    }
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    public virtual void Update<TModel>(TModel model, bool isLogical = false) where TModel : class, IEntitySet<TKey>
    {
        if (isLogical && model is IEntitySetWithCreate<TKey>)
        {
            //逻辑修改会产生一个当前数据不同主键的副本, 并设置为已更新的状态, 使用 RowVersion 字段关联更新后的数据编号
            var logicalCopy = model.Copy();
            logicalCopy.Id = KeyGenerator.GetKey<TKey>();
            if (logicalCopy is IEntitySetWithCreate<TKey> entitySetWithCreate)
            {
                entitySetWithCreate.RowVersion = model.Id;
                entitySetWithCreate.State = DataState.Updated;
            }

            _context.Set<TModel>().Add(logicalCopy);
        }
    }
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual void Delete<TModel>(TModel model, bool isLogical = false) where TModel : class, IEntitySet<TKey>
    {
        if (isLogical && model is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            Update(entitySetWithCreate);
            entitySetWithCreate.State = DataState.Deleted;
            _context.Set<TModel>().Update(model);
        }
        else
        {
            _context.Set<TModel>().Remove(model);
        }
    }
    #endregion

    #region 数据查询方法, 面向当前业务类型
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <returns></returns>
    public virtual IQueryable<TEntitySet> GetQueryable()
    {
        if (EnableCache)
        {
            var cacheKey = AppSettings.CACHE_DATA + _type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = _cache.GetValue<IEnumerable<TEntitySet>>(cacheKey);
            if (entitySets == null)
            {
                _logger.LogInformation("{type} have no cache data, will query and cached", _typeName);
                entitySets = _dbSet.ToHashSet();
                _cache.SetValue(cacheKey, entitySets);
            }

            return entitySets.AsQueryable();
        }

        _logger.LogDebug("The user {user} query {typeName}", UserName, _typeName);
        return _dbSet;
    }
    /// <summary>
    /// 非注入类型 TEntity 统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual IQueryable<TEntitySet> GetQueryable(Expression<Func<TEntitySet, bool>> predicate) => GetQueryable().Where(predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual async Task<TEntitySet?> GetAsync(TKey id) => await GetQueryable().FirstOrDefaultAsync(x => x.Id.Equals(id));
    /// <summary>
    /// TODO TBD 此方法核心功能尚未经过完整测试
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <returns></returns>
    public virtual async Task<PagedResult> GetPagedResultAsync()
    {
        var queryable = GetQueryable();
        var queryOptions = new QueryOptions<TEntitySet>(new QueryOptionRawValue(_request.Query));
        if (queryOptions.Filter != null)
        {
            if (queryOptions.Filter is NestedFilterOption<TEntitySet> filter && filter.Filters != null && filter.Filters.Any())
            {
                //目前仅支持一级嵌套, 且顶级条件仅支持 and
                //var expression = string.Join(GetFilterLogic(filter.Logic ?? LogicType.And), filter.Filters.Select(x => GetFilterExpression(x)));
                queryable = queryable.Where(string.Join(filter.GetFilterLogic(), filter.Filters.Select(x => x.GetFilterExpression())));
            }
            else if ((!string.IsNullOrWhiteSpace(queryOptions.Filter.Field)) && (!string.IsNullOrWhiteSpace(queryOptions.Filter.Value)))
            {
                queryable = queryable.Where(queryOptions.Filter.GetFilterExpression(), queryOptions.Filter.Value);
            }
        }

        if (queryOptions.Orderby != null)
        {
            queryable = queryable.OrderBy(queryOptions.Orderby.ToString());
        }

        var result = new PagedResult();
        if (queryOptions.Count.HasValue && queryOptions.Count.Value)
        {
            result.Total = await queryable.CountAsync();
        }

        if (queryOptions.Skip.HasValue && queryOptions.Skip.Value > 0)
        {
            queryable = queryable.Skip(queryOptions.Skip.Value);
        }

        if (queryOptions.Top.HasValue && queryOptions.Top.Value > 0)
        {
            queryable = queryable.Take(queryOptions.Top.Value);
            result.PageSize = queryOptions.Top.Value;
        }

        result.PageIndex = (queryOptions.Skip ?? 1) / result.PageSize + 1;

        if (queryOptions.Select != null)
        {
            var columns = queryOptions.Select.ToString<string>();
            result.Rows = await queryable.Select("new {" + columns + "}").ToDynamicListAsync();
        }
        else
        {
            result.Rows = await queryable.ToListAsync();
        }

        return result;
    }
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfExistAsync(TKey? id)
    {
        if (id == null || id.Equals(default))
        {
            return;
        }

        if (await GetQueryable().AnyAsync(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录已存在!");
        }
    }
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task<TEntitySet> GetIfExistAsync(TKey id)
    {
        var entitySet = await GetAsync(id);
        if (entitySet == null)
        {
            throw new BadHttpRequestException($"{_typeName} 中 id 等于 {id} 的记录不存在!");
        }

        return entitySet;
    }
    /// <summary>
    /// 获取业务数据的集合成员, 成员是附加信息, 附件及特定类型的附属集合
    /// </summary>
    /// <typeparam name="TElement">附加信息, 附件或附属集合的类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public virtual IEnumerable<TElement>? GetCollectionMembers<TElement>(TEntitySet entitySet) where TElement : class, IEntitySet<TKey>
    {
        var propertyInfo = _propertyInfos.FirstOrDefault(x => x.PropertyType.IsFromType<IEnumerable<TElement>>());
        if (propertyInfo != null && propertyInfo.GetValue(entitySet) is IEnumerable<TElement> collection)
        {
            return collection;
        }

        return null;
    }
    #endregion

    #region 数据查询方法, 面向非当前业务类型
    /// <summary>
    /// 非注入类型 TModel 的统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// 缓存的前提是全局缓存开关开启且当前数据对象类型标记为可缓存
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <returns></returns>
    public virtual IQueryable<TModel> GetQueryable<TModel>() where TModel : class
    {
        var _type = typeof(TModel);
        var _typeName = typeof(TModel).GetDisplayName();
        var queryable = _context.Set<TModel>();
        if (_appSetting.EnableCache && _type.HasAttribute<CacheableAttribute>())
        {
            var cacheKey = AppSettings.CACHE_DATA + _type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = _cache.GetValue<IEnumerable<TModel>>(cacheKey);
            if (entitySets == null)
            {
                _logger.LogInformation("{typeName} have no cache data, will query and cached", _typeName);
                entitySets = queryable.ToHashSet();
                _cache.SetValue(cacheKey, entitySets);
            }

            return entitySets.AsQueryable();
        }

        _logger.LogDebug("The user {user} query {typeName}", UserName, _typeName);
        return queryable;
    }
    /// <summary>
    /// 非注入类型 TModel 的统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// 缓存的前提是全局缓存开关开启且当前数据对象类型标记为可缓存
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual IQueryable<TModel> GetQueryable<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class => GetQueryable<TModel>().Where(predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <param name="id"></param>
    /// <returns></returns>
    public virtual async Task<TModel?> GetAsync<TModel>(TKey id) where TModel : class, IEntitySet<TKey> => await GetQueryable<TModel>().FirstOrDefaultAsync(x => x.Id.Equals(id));
    /// <summary>
    /// TODO TBD 此方法核心功能尚未经过完整测试
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <returns></returns>
    public virtual async Task<PagedResult> GetPagedResultAsync<TModel>() where TModel : class, IEntitySet<TKey>
    {
        var queryable = GetQueryable<TModel>();
        var queryOptions = new QueryOptions<TModel>(new QueryOptionRawValue(_httpContext.Request.Query));
        if (queryOptions.Filter != null)
        {
            if (queryOptions.Filter is NestedFilterOption<TModel> filter && filter.Filters != null && filter.Filters.Any())
            {
                //目前仅支持一级嵌套, 且顶级条件仅支持 and
                //var expression = string.Join(GetFilterLogic(filter.Logic ?? LogicType.And), filter.Filters.Select(x => GetFilterExpression(x)));
                queryable = queryable.Where(string.Join(filter.GetFilterLogic(), filter.Filters.Select(x => x.GetFilterExpression())));
            }
            else if ((!string.IsNullOrWhiteSpace(queryOptions.Filter.Field)) && (!string.IsNullOrWhiteSpace(queryOptions.Filter.Value)))
            {
                queryable = queryable.Where(queryOptions.Filter.GetFilterExpression(), queryOptions.Filter.Value);
            }
        }

        if (queryOptions.Orderby != null)
        {
            queryable = queryable.OrderBy(queryOptions.Orderby.ToString());
        }

        var result = new PagedResult();
        if (queryOptions.Count.HasValue && queryOptions.Count.Value)
        {
            result.Total = await queryable.CountAsync();
        }

        if (queryOptions.Skip.HasValue && queryOptions.Skip.Value > 0)
        {
            queryable = queryable.Skip(queryOptions.Skip.Value);
        }

        if (queryOptions.Top.HasValue && queryOptions.Top.Value > 0)
        {
            queryable = queryable.Take(queryOptions.Top.Value);
            result.PageSize = queryOptions.Top.Value;
        }

        result.PageIndex = (queryOptions.Skip ?? 1) / result.PageSize + 1;

        if (queryOptions.Select != null)
        {
            var columns = queryOptions.Select.ToString<string>();
            result.Rows = await queryable.Select("new {" + columns + "}").ToDynamicListAsync();
        }
        else
        {
            result.Rows = await queryable.ToListAsync();
        }

        return result;
    }
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfExistAsync<TModel>(TKey? id) where TModel : class, IEntitySet<TKey>
    {
        if (id == null || id.Equals(default))
        {
            return;
        }

        if (await GetQueryable<TModel>().AnyAsync(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{typeof(TModel).GetDisplayName()} 中当前记录已存在!");
        }
    }
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task<TModel> GetIfExistAsync<TModel>(TKey id) where TModel : class, IEntitySet<TKey>
    {
        var entitySet = await GetAsync<TModel>(id);
        if (entitySet == null)
        {
            throw new BadHttpRequestException($"{typeof(TModel).GetDisplayName()} 中当前记录不存在!");
        }

        return entitySet;
    }
    /// <summary>
    /// 获取业务数据的集合成员, 成员是附加信息, 附件及特定类型的附属集合
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <typeparam name="TElement">附加信息, 附件或附属集合的类型</typeparam>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    public virtual IEnumerable<TElement>? GetCollectionMembers<TModel, TElement>(TModel entitySet) where TModel : class, IEntitySet<TKey> where TElement : class, IEntitySet<TKey>
    {
        var propertyInfo = typeof(TModel).GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(x => x.PropertyType.IsFromType<IEnumerable<TElement>>());
        if (propertyInfo != null && propertyInfo.GetValue(entitySet) is IEnumerable<TElement> collection)
        {
            return collection;
        }

        return null;
    }
    #endregion

    #region 数据操作方法, 面向当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task UniqueAsync(TEntitySet entitySet)
    {
        var propertyInfos = _propertyInfos.Where(x => x.HasAttribute<UniqueAttribute>());
        if (propertyInfos.IsNotNullOrEmpty())
        {
            var index = 0;
            var keys = new List<string>();
            var values = new List<object>();
            var messages = new List<string>();

            if (!(entitySet.Id == null || entitySet.Id.Equals(default)))
            {
                keys.Add($"{nameof(entitySet.Id)} != @{index++}");
                values.Add($"{entitySet.Id}");
            }

            foreach (var propertyInfo in propertyInfos)
            {
                var value = propertyInfo.GetValue(entitySet);
                keys.Add($"{propertyInfo.Name} == @{index++}");
                values.Add($"{value}");
                messages.Add($"<{propertyInfo.GetDisplayName()}> = {value}");
            }

            var predicate = string.Join(" and ", keys);
            var exist = await GetQueryable<TEntitySet>().AnyAsync(predicate, values.ToArray());
            if (exist)
            {
                throw new BadHttpRequestException($"{_typeName} 中 {messages.ToString<string>()} 的记录已经存在!");
            }
        }
    }
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        _logger.LogDebug("The user {user} will add {typeName}, the content is: " + entitySet.ToJsonString(), UserName, _typeName);

        await ThrowIfExistAsync(entitySet.Id);
        await UniqueAsync(entitySet);
        Create(entitySet);
        Update(entitySet);
        Detach(entitySet);
        await _dbSet.AddAsync(entitySet);
        await SaveChangesAsync(context);

        return entitySet;
    }
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作, 自动验证重复
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        _logger.LogDebug("The user {user} will {isLogical} update {typeName}, the content is: " + entitySet.ToJsonString(), UserName, GetLogicalString(isLogical), _typeName);

        await UniqueAsync(entitySet);
        var entity = await GetIfExistAsync(entitySet.Id);

        //先对原数据进行逻辑更新操作
        Update(entity, isLogical);

        //修改时先查了一次当前对象, 因此不能取消附加
        //Detach(entity);
        entitySet.Adapt(entity, context?.AdapterConfig ?? GetAdapterConfig());
        Update(entity);
        _dbSet.Update(entity);
        await SaveChangesAsync(context);

        return entitySet;
    }
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync(TKey id, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        var entitySet = await GetIfExistAsync(id);
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName}, the content is: " + entitySet.ToJsonString(), UserName, GetLogicalString(isLogical), _typeName);

        Delete(entitySet, isLogical);
        await SaveChangesAsync(context);
    }
    #endregion

    #region 数据操作方法, 面向非当前业务类型
    /// <summary>
    /// 新增或修改阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TModel">查询的类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task UniqueAsync<TModel>(TModel model) where TModel : class, IEntitySet<TKey>
    {
        var _type = typeof(TModel);
        var propertyInfos = _type.GetProperties().Where(x => x.HasAttribute<UniqueAttribute>());
        if (propertyInfos.IsNotNullOrEmpty())
        {
            var index = 0;
            var keys = new List<string>();
            var values = new List<object>();
            var messages = new List<string>();

            if (!(model.Id == null || model.Id.Equals(default)))
            {
                keys.Add($"{nameof(model.Id)} != @{index++}");
                values.Add($"{model.Id}");
            }

            foreach (var propertyInfo in propertyInfos)
            {
                var value = propertyInfo.GetValue(model);
                keys.Add($"{propertyInfo.Name} == @{index++}");
                values.Add($"{value}");
                messages.Add($"<{propertyInfo.GetDisplayName()}> = {value}");
            }

            var predicate = string.Join(" and ", keys);
            var exist = await GetQueryable<TModel>().AnyAsync(predicate, values.ToArray());
            if (exist)
            {
                throw new BadHttpRequestException($"{_type.GetDisplayName()} 中 {messages.ToString<string>()} 的记录已经存在!");
            }
        }
    }
    /// <summary>
    /// 重载基类的新增方法, 完成自动业务数据及其视图模型的新增和保存
    /// 如果 model 的类型为 ViewModel, 则认为是当前实体数据对象的视图类型, 使用当前类型的方法处理, 否则认为是其他实体对象模型的类型, 使用基类方法处理
    /// </summary>
    /// <typeparam name="TModel">业务数据或其视图类型</typeparam>
    /// <param name="model">业务数据或其视图</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TModel> AddAsync<TModel>(TModel model, DataServiceContext? context = default) where TModel : class, IEntitySet<TKey>
    {
        if (model is IViewModel<TKey> viewModel)
        {
            await AddAsync(viewModel.Adapt<TEntitySet>(context?.AdapterConfig ?? GetAdapterConfig()), context);
        }
        else
        {
            _logger.LogDebug("The user {user} will add {typeName}, the content is: " + model.ToJsonString(), UserName, typeof(TModel).Name);

            await ThrowIfExistAsync<TModel>(model.Id);
            await UniqueAsync(model);
            Create(model);
            Update(model);
            Detach(model);
            await _context.Set<TModel>().AddAsync(model);
            await SaveChangesAsync(context);
        }

        return model;
    }
    /// <summary>
    /// 重载基类的修改方法, 完成自动业务数据及其视图模型的修改和保存
    /// </summary>
    /// <typeparam name="TModel">业务数据或其视图类型</typeparam>
    /// <param name="model">业务数据或其视图</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TModel> UpdateAsync<TModel>(TModel model, DataServiceContext? context = default) where TModel : class, IEntitySet<TKey>
    {
        if (model is IViewModel<TKey> viewModel)
        {
            await UpdateAsync(viewModel.Adapt<TEntitySet>(context?.AdapterConfig ?? GetAdapterConfig()), context);
        }
        else
        {
            var isLogical = context?.IsLogical ?? false;
            _logger.LogDebug("The user {user} will {isLogical} update {typeName}, the content is: " + model.ToJsonString(), UserName, GetLogicalString(isLogical), _typeName);

            await UniqueAsync(model);
            var entity = await GetIfExistAsync<TModel>(model.Id);

            //先对原数据进行逻辑更新操作
            Update(entity, isLogical);

            //修改时先查了一次当前对象, 因此不能取消附加
            //Detach(entity);
            model.Adapt(entity, context?.AdapterConfig ?? GetAdapterConfig());
            Update(entity);
            _context.Set<TModel>().Update(entity);
            await SaveChangesAsync(context);
        }

        return model;
    }
    /// <summary>
    /// 重载基类的删除记录, 完成自动业务数据及其视图模型的删除
    /// </summary>
    /// <typeparam name="TModel">业务数据或其视图类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync<TModel>(TKey id, DataServiceContext? context = default) where TModel : class, IEntitySet<TKey>
    {
        var isLogical = context?.IsLogical ?? false;
        var model = await GetIfExistAsync<TModel>(id);
        _logger.LogDebug("The user {user} will {isLogical} delete {typeName}, the content is: " + model.ToJsonString(), UserName, GetLogicalString(isLogical), _typeName);

        Delete(model, isLogical);
        await SaveChangesAsync(context);
    }
    #endregion

    #region 内部成员方法
    /// <summary>
    /// 提交当前操作的内部包装方法, 包含判断是否自动提交
    /// </summary>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    protected virtual async Task<int> SaveChangesAsync(DataServiceContext? context = default)
    {
        context?.Before?.Invoke();
        if (context?.AutoSaveChanges ?? true)
        {
            var result = await SaveChangesAsync();
            if (result > 0)
            {
                _logger.LogInformation("The user {user} save change {typeName} to database success {isLogical}", UserName, _typeName, GetLogicalString(context?.IsLogical ?? false));
                await RefreshCacheAsync();
                context?.After?.Invoke();
            }

            return result;
        }

        return 0;
    }
    /// <summary>
    /// 获取是否逻辑操作的字符串
    /// </summary>
    /// <param name="isLogical"></param>
    /// <returns></returns>
    protected virtual string GetLogicalString(bool isLogical = false) => isLogical ? "logical" : "physical";
    /// <summary>
    /// 获取第一个 BusinessType 特性标记的属性的 BusinessType Name
    /// 返回 BusinessType 的值
    /// </summary>
    /// <returns></returns>
    protected virtual string GetBusinessType()
    {
        var businessType = _type.Name;
        var propertyInfo = _propertyInfos.FirstOrDefault(x => x.HasAttribute<BusinessTypeAttribute>());
        if (propertyInfo != null)
        {
            businessType = propertyInfo.GetAttributeValue<BusinessTypeAttribute, string>(nameof(BusinessTypeAttribute.Name));
            businessType ??= _type.Name + AppSettings.CHAR_DOT + propertyInfo.Name;
        }

        return businessType;
    }
    /// <summary>
    /// 获取第一个 BusinessType 特性标记的属性的 BusinessType Name
    /// 返回 BusinessType 的值
    /// </summary>
    /// <typeparam name="TModel">业务数据或其视图类型</typeparam>
    /// <returns></returns>
    protected virtual string GetBusinessType<TModel>() where TModel : class, IEntitySet<TKey>
    {
        var type = typeof(TModel);
        var businessType = type.Name;
        var propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(x => x.HasAttribute<BusinessTypeAttribute>());
        if (propertyInfo != null)
        {
            businessType = propertyInfo.GetAttributeValue<BusinessTypeAttribute, string>(nameof(BusinessTypeAttribute.Name));
            businessType ??= type.Name + AppSettings.CHAR_DOT + propertyInfo.Name;
        }

        return businessType;
    }
    /// <summary>
    /// 获取全部 BusinessType 特性标记的属性的 BusinessType Name 集合
    /// 返回属性 和 BusinessType 的值的集合
    /// </summary>
    /// <returns></returns>
    protected virtual IEnumerable<KeyValuePair<string, string>> GetBusinessTypes()
    {
        var dic = new Dictionary<string, string>();
        var propertyInfos = _propertyInfos.Where(x => x.HasAttribute<BusinessTypeAttribute>());
        if (propertyInfos != null && propertyInfos.Any())
        {
            foreach (var propertyInfo in propertyInfos)
            {
                var businessType = propertyInfo.GetAttributeValue<BusinessTypeAttribute, string>(nameof(BusinessTypeAttribute.Name));
                businessType ??= _type.Name + AppSettings.CHAR_DOT + propertyInfo.Name;
                dic.Add(propertyInfo.Name, businessType);
            }
        }

        return dic;
    }
    /// <summary>
    /// 获取全部 BusinessType 特性标记的属性的 BusinessType Name 集合
    /// 返回属性 和 BusinessType 的值的集合
    /// </summary>
    /// <typeparam name="TModel">业务数据或其视图类型</typeparam>
    /// <returns></returns>
    protected virtual IEnumerable<KeyValuePair<string, string>> GetBusinessTypes<TModel>() where TModel : class, IEntitySet<TKey>
    {
        var type = typeof(TModel);
        var dic = new Dictionary<string, string>();
        var propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.HasAttribute<BusinessTypeAttribute>());
        if (propertyInfos != null && propertyInfos.Any())
        {
            foreach (var propertyInfo in propertyInfos)
            {
                var businessType = propertyInfo.GetAttributeValue<BusinessTypeAttribute, string>(nameof(BusinessTypeAttribute.Name));
                businessType ??= type.Name + AppSettings.CHAR_DOT + propertyInfo.Name;
                dic.Add(propertyInfo.Name, businessType);
            }
        }

        return dic;
    }
    #endregion
}

/// <summary>
/// 公共数据处理服务
/// 字符串作为键的默认实现
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
public class DataService<TDbContext, TEntitySet> :
    DataService<TDbContext, TEntitySet, string>,
    IDataService<TDbContext, TEntitySet>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<string>
{
    public DataService(
        TDbContext context,
        ILogger<DataService<TDbContext, TEntitySet>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        ) : base(context, logger, cache, httpContextAccessor, appSetting) { }
}